package com.mg.plugin.agrometeorology.model;

import com.mg.model.Model;
import com.mg.model.Param;
import com.mg.model.Port;
import com.mg.plugin.agrometeorology.common.MathCalculator;
import com.mg.plugin.agrometeorology.entity.WeaData;
import com.mg.pluginexample2.ModuleBase;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class ProductionForecastBaseFactorDataAssistModel extends ModuleBase {

    public ProductionForecastBaseFactorDataAssistModel(int nID) {
        super(nID);
        m_alInputPort.add(new Port(this, Port.Type.Unknown));
        m_alInputPort.add(new Port(this, Port.Type.Unknown));
        m_alOutputPort.add(new Port(this, Port.Type.Unknown));
    }

    public String GetGroupName() {
        return "产量预报";
    }

    public String GetName() {
        return "基于关键气象因子影响指数方法(产量)";
    }

    public String GetDescription() {
        return "基于关键气象因子影响指数方法（数据必须是同一年的）";
    }

    public boolean OnAttach(Port portFrom, Port portTo) {
        try {
            int i = FindPort(portFrom, false);
            if (i != -1) {
                return true;
            }
            i = FindPort(portTo, true);
            if (i == -1) {
                return false;
            }

            ArrayList<Param> alParam = new ArrayList<Param>();
            portFrom.GetModule().GetOutputParam(portFrom, alParam);

            if (alParam.size() != 1) {
                return false;
            }

            if (alParam.get(0).m_objValue != null && !(alParam.get(0).m_objValue instanceof ArrayList)) {
                m_model.OutputLog(Model.LogLevel.Info, "alParam.get(0).m_objValue != null && !(alParam.get(0).m_objValue instanceof ArrayList)");
                return false;
            }
            if (i == 0) {
                m_model.OutputLog(Model.LogLevel.Info, "SET m_inputStationDataList");
                m_inputStationDataList = (List<String>) alParam.get(0).m_objValue;
            }
            if (i == 1) {
                m_model.OutputLog(Model.LogLevel.Info, "m_inputRealityList" + alParam.get(0).m_objValue.getClass());
                m_inputRealityList = (List<String>) alParam.get(0).m_objValue;
            }

            portTo.SetType(portFrom.GetType());
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }

    public boolean OnDetach(Port portFrom, Port portTo) {
        if (!super.OnDetach(portFrom, portTo))
            return false;
        try {
            int i = FindPort(portTo, true);
            if (i == -1)
                return true;
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }

    //用于 参数->XML 等
    public int GetParam(ArrayList<Param> alParam) {
        try {
            super.GetParam(alParam);
            alParam.add(new Param("Date", m_strDate, "时间对应字段", "Date", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("DaDouPrecipitation", m_strDadouRain, "DaDou降水对应字段", "Precipitation", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("YuMiPrecipitation", m_strYumiRain, "YuMi降水对应字段", "Precipitation", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("ShuiDaoPrecipitation", m_strShuiRain, "ShuiDao降水对应字段", "Precipitation", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("DaDouTemperature", m_strDadouTemp, "DaDou温度对应字段", "Temperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("YuMiTemperature", m_strYumiTemp, "YuMi温度对应字段", "Temperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("ShuiDaoTemperature", m_strShuiTemp, "ShuiDao温度对应字段", "Temperature", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("DaDouSunshineHour", m_strDadouSun, "DaDou日照时数对应字段", "SunshineHour", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("YuMiSunshineHour", m_strYumiSun, "YuMi日照时数对应字段", "SunshineHour", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("ShuiDaoSunshineHour", m_strShuiSun, "ShuiDao日照时数对应字段", "SunshineHour", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("m_strDadou", m_strDadou, "大豆单产", "m_strDadou", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("m_strYumi", m_strYumi, "玉米单产", "m_strYumi", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("m_strShuiDao", m_strShuiDao, "水稻单产", "m_strShuiDao", "参数匹配", Param.EditType.Default));
            alParam.add(new Param("m_strStartTime", m_strStartTime, "数据开始时间", "Time", "时间", Param.EditType.Default));
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return alParam.size();
    }

    public boolean OnParamChanged(ArrayList<Param> alParam, int nIndex, Object objValue) {
        return super.OnParamChanged(alParam, nIndex, objValue);
    }

    //用于 XML->参数 等
    public int SetParam(final ArrayList<Param> alParam) {
        int i = super.SetParam(alParam);
        try {
            m_strDate = (String) alParam.get(i++).m_objValue;
            m_strDadouRain = (String) alParam.get(i++).m_objValue;
            m_strYumiRain = (String) alParam.get(i++).m_objValue;
            m_strShuiRain = (String) alParam.get(i++).m_objValue;
            m_strDadouTemp = (String) alParam.get(i++).m_objValue;
            m_strYumiTemp = (String) alParam.get(i++).m_objValue;
            m_strShuiTemp = (String) alParam.get(i++).m_objValue;
            m_strDadouSun = (String) alParam.get(i++).m_objValue;
            m_strYumiSun = (String) alParam.get(i++).m_objValue;
            m_strShuiSun = (String) alParam.get(i++).m_objValue;
            m_strDadou = (String) alParam.get(i++).m_objValue;
            m_strYumi = (String) alParam.get(i++).m_objValue;
            m_strShuiDao = (String) alParam.get(i++).m_objValue;
            m_strStartTime = (String) alParam.get(i++).m_objValue;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return i;
    }

    public int GetOutputParam(Port port, ArrayList<Param> alParam) {
        int i = FindPort(port, false);
        if (i == -1) {
            return 0;
        }

        alParam.add(new Param("ResultString", m_listCSV));
        return alParam.size();
    }

    @SuppressWarnings("static-access")
    public boolean Execute() {
        if (m_inputRealityList != null && m_inputRealityList.size() >= 2) {
            m_inputRealityList.remove(1);
            m_inputRealityList.remove(0);
            m_inputStationDataList.addAll(m_inputRealityList);
        }

        m_listCSV.clear();
        String head = "id,endDate,forecastType,dadouForecastRate,yumiForecastRate,shuidaoForecastRate,dadouForecast,yumiForecast,shuidaoForecast,dadouFiveForecastRate,yumiFiveForecastRate,shuidaoFiveForecastRate";
        String type = "String,String,String,Double,Double,Double,Double,Double,Double,Double,Double,Double";
        m_listCSV.add(head);
        m_listCSV.add(type);
        WeaData calcWeaData = null;
        List<WeaData> weaDataList = new ArrayList<WeaData>();

        SimpleDateFormat sdfYear = new SimpleDateFormat("yyyy");
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        String str = m_strStartTime.replace("$ThisYear", sdfYear.format(m_model.m_dateDataStart));
        str = str.replace("$DateStart", sdfDate.format(m_model.m_dateDataStart));
        m_model.OutputLog(Model.LogLevel.Info, "STR" + str);
        Calendar dateNow = Calendar.getInstance();
        setDate(dateNow, sdfDate, str);

        if (m_inputStationDataList != null && m_inputStationDataList.size() > 0) {
            String foreHeader = m_inputStationDataList.get(0);
            if (!verifyHeader(foreHeader)) {
                m_model.OutputLog(Model.LogLevel.Error, "Header Can Not Match!");
                return false;
            } else {
                calcWeaData = formatStationData(m_inputStationDataList, foreHeader, weaDataList, dateNow, sdfDate);
            }
        } else {
            return false;
        }

        double[][] dadouVir = new double[4][5];
        double[][] yumiVir = new double[4][5];
        double[][] shuidaoVir = new double[4][5];
        for (int i = 0; i < 4; i++) {
            WeaData tmp = weaDataList.get(i);
            dadouVir[i][0] = tmp.getrDadouTemp();
            dadouVir[i][1] = tmp.getrDadouRain();
            dadouVir[i][2] = tmp.getrDadouSun();
            dadouVir[i][3] = 1;
            dadouVir[i][4] = tmp.getDadou();

            yumiVir[i][0] = tmp.getrYumiTemp();
            yumiVir[i][1] = tmp.getrYumiRain();
            yumiVir[i][2] = tmp.getrYumiSun();
            yumiVir[i][3] = 1;
            yumiVir[i][4] = tmp.getYumi();

            shuidaoVir[i][0] = tmp.getrShuiDaoTemp();
            shuidaoVir[i][1] = tmp.getrShuiDaoRain();
            shuidaoVir[i][2] = tmp.getrShuiDaoSun();
            shuidaoVir[i][3] = 1;
            shuidaoVir[i][4] = tmp.getShuidao();
        }
        WeaData oldWeaData = weaDataList.get(0);
        double[] forecastRate = calcForecastRate(dadouVir, yumiVir, shuidaoVir, calcWeaData, oldWeaData);

        double[] fiveYield = calcYield(weaDataList);
        String id = str + "_FACTOR";


        String data = id + "," + str + "," + "FACTOR," + forecastRate[0] + "," + forecastRate[1] + "," + forecastRate[2]
                + "," + oldWeaData.getDadou() * (1 + forecastRate[0])
                + "," + oldWeaData.getYumi() * (1 + forecastRate[1])
                + "," + oldWeaData.getShuidao() * (1 + forecastRate[2])
                + "," + (oldWeaData.getDadou() * (1 + forecastRate[0]) - fiveYield[0]) / fiveYield[0]
                + "," + (oldWeaData.getYumi() * (1 + forecastRate[1]) - fiveYield[1]) / fiveYield[1]
                + "," + (oldWeaData.getShuidao() * (1 + forecastRate[2]) - fiveYield[2]) / fiveYield[2];
        m_listCSV.add(data);
        OnParamChanged();
        return true;
    }

    private double[] calcForecastRate(double[][] dadouVir, double[][] yumiVir, double[][] shuidaoVir, WeaData calcWeaData, WeaData oldWeaData) {
        double[] dadou = MathCalculator.calcDeterminant(dadouVir);
        double[] yumi = MathCalculator.calcDeterminant(yumiVir);
        double[] shuidao = MathCalculator.calcDeterminant(shuidaoVir);
        double dRate = dadou[0] * calcWeaData.getrDadouTemp() + dadou[1] * calcWeaData.getrDadouRain() + dadou[2] * calcWeaData.getrDadouSun() + dadou[3];
        double yRate = yumi[0] * calcWeaData.getrYumiTemp() + yumi[1] * calcWeaData.getrYumiRain() + yumi[2] * calcWeaData.getrYumiSun() + yumi[3];
        double sRate = shuidao[0] * calcWeaData.getrShuiDaoTemp() + shuidao[1] * calcWeaData.getrShuiDaoRain() + shuidao[2] * calcWeaData.getrShuiDaoSun() + shuidao[3];


        dRate = (dRate - oldWeaData.getDadou()) / oldWeaData.getDadou();
        yRate = (yRate - oldWeaData.getYumi()) / oldWeaData.getYumi();
        sRate = (sRate - oldWeaData.getShuidao()) / oldWeaData.getShuidao();
//        double d = new BigDecimal(dRate).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
//        double y = new BigDecimal(yRate).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
//        double s = new BigDecimal(sRate).setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
//        double[] res = {dRate - d, yRate - y, sRate - s};
        double[] res = {dRate, yRate, sRate};
        return res;
    }

    private double[] calcYield(List<WeaData> weaDataList) {
        double dadouRate = 0;
        double yumiRate = 0;
        double shuidaoRate = 0;
        for (WeaData weaData : weaDataList) {
            dadouRate += weaData.getDadou();
            yumiRate += weaData.getYumi();
            shuidaoRate += weaData.getShuidao();
        }
        double[] rate = {dadouRate / 5, yumiRate / 5, shuidaoRate / 5};
        return rate;
    }

    private void setDate(Calendar dateNow, SimpleDateFormat sdfDate, String str) {
        try {
            dateNow.setTime(sdfDate.parse(str));
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    private WeaData formatStationData(List<String> inputRealityList, String realHeader, List<WeaData> weaDataList, Calendar dateNow, SimpleDateFormat sdfDate) {
        WeaData resWeaData = null;
        Calendar calendar = Calendar.getInstance();
        int realDateIndex = findDataIndex(realHeader, m_strDate);

        int realDadouTmpIndex = findDataIndex(realHeader, m_strDadouTemp);
        int realDadouRainIndex = findDataIndex(realHeader, m_strDadouRain);
        int realDadouSunIndex = findDataIndex(realHeader, m_strDadouSun);

        int realYumiTmpIndex = findDataIndex(realHeader, m_strYumiTemp);
        int realYumiRainIndex = findDataIndex(realHeader, m_strYumiRain);
        int realYumiSunIndex = findDataIndex(realHeader, m_strYumiSun);

        int realShuiDaoTmpIndex = findDataIndex(realHeader, m_strShuiTemp);
        int realShuiDaoRainIndex = findDataIndex(realHeader, m_strShuiRain);
        int realShuiDaoSunIndex = findDataIndex(realHeader, m_strShuiSun);

        int realDadouIndex = findDataIndex(realHeader, m_strDadou);
        int realYumiIndex = findDataIndex(realHeader, m_strYumi);
        int realShuiDaoIndex = findDataIndex(realHeader, m_strShuiDao);

        String date = "";
        int yearNow = dateNow.get(Calendar.YEAR);
        for (int i = 2; i < inputRealityList.size(); i++) {
            String[] data = inputRealityList.get(i).split(",");

            date = data[realDateIndex];
            setDate(calendar, sdfDate, date);
            int day = calendar.get(Calendar.DAY_OF_MONTH);
            int month = calendar.get(Calendar.MONTH);
            int year = calendar.get(Calendar.YEAR);

            WeaData weaData = new WeaData();
            weaData.setDate(date);
            weaData.setYear(String.valueOf(year));
            weaData.setMMDD(month + "-" + day);
            weaData.setrDadouTemp(Float.parseFloat(data[realDadouTmpIndex]));
            weaData.setrDadouRain(Float.parseFloat(data[realDadouRainIndex]));
            weaData.setrDadouSun(Float.parseFloat(data[realDadouSunIndex]));

            weaData.setrYumiTemp(Float.parseFloat(data[realYumiTmpIndex]));
            weaData.setrYumiRain(Float.parseFloat(data[realYumiRainIndex]));
            weaData.setrYumiSun(Float.parseFloat(data[realYumiSunIndex]));

            weaData.setrShuiDaoTemp(Float.parseFloat(data[realShuiDaoTmpIndex]));
            weaData.setrShuiDaoRain(Float.parseFloat(data[realShuiDaoRainIndex]));
            weaData.setrShuiDaoSun(Float.parseFloat(data[realShuiDaoSunIndex]));

            if (!data[realDadouIndex].equals("null")) {
                weaData.setDadou(Float.parseFloat(data[realDadouIndex]));
                weaData.setYumi(Float.parseFloat(data[realYumiIndex]));
                weaData.setShuidao(Float.parseFloat(data[realShuiDaoIndex]));
            }

            if (yearNow == year) {
                resWeaData = weaData;
            } else {
                weaDataList.add(weaData);
            }

        }
        return resWeaData;
    }

    /**
     * 表头检验
     *
     * @param realHeader 实况数据表头
     * @return
     */
    private boolean verifyHeader(String realHeader) {

        if (realHeader.contains(m_strDate) && realHeader.contains(m_strDadou)) {
            if (realHeader.contains(m_strYumi) && realHeader.contains(m_strShuiDao)) {
                return true;
            }
        }
        return false;
    }

    private int findDataIndex(String realHeader, String strRain) {

        String[] data = realHeader.split(",");
        for (int i = 0; i < data.length; i++) {
            if (data[i].equalsIgnoreCase(strRain)) {
                return i;
            }
        }
        return -1;
    }

    private List<String> m_inputStationDataList = new ArrayList<String>();
    private List<String> m_inputRealityList = new ArrayList<String>();
    private List<String> m_listCSV = new ArrayList<String>();

    private String m_strDate;
    private String m_strDadouRain;
    private String m_strYumiRain;
    private String m_strShuiRain;
    private String m_strDadouTemp;
    private String m_strYumiTemp;
    private String m_strShuiTemp;
    private String m_strDadouSun;
    private String m_strYumiSun;
    private String m_strShuiSun;
    private String m_strDadou;
    private String m_strYumi;
    private String m_strShuiDao;
    private String m_strStartTime;
}
